package org.yu.register.capability;

import com.google.common.collect.HashMultimap;
import com.google.common.collect.Multimap;
import com.mojang.datafixers.util.Pair;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.TranslatableComponent;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attribute;
import net.minecraft.world.entity.ai.attributes.AttributeModifier;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.common.util.INBTSerializable;
import org.apache.commons.lang3.builder.EqualsBuilder;
import org.apache.commons.lang3.builder.HashCodeBuilder;
import org.apache.http.util.TextUtils;
import org.jetbrains.annotations.Nullable;
import org.yu.register.MobEffectRegister;

import java.util.*;

public class ItemMixedCapability implements INBTSerializable<CompoundTag> {
    protected final HashMap<String,Integer> attributeMap=new HashMap<>();
    protected final HashMap<String,Integer> itemMixedNames=new HashMap<>();

    public HashMap<String, Integer> getItemMixedNames() {
        return itemMixedNames;
    }

    public static final Holder VITALITY=new Holder("vitality", "ee64ab10-feda-4cd7-9951-a05eef8da014"){
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            return simpleAttribute(Attributes.MAX_HEALTH,level*2, AttributeModifier.Operation.ADDITION);
        }
    };//活力，加生命上限.
    public static final Holder STILLNESS= new Holder("stillness", "2182493d-9e6f-4c6f-a6fe-2460dd51b2b3") {
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            return simpleAttribute(Attributes.MAX_HEALTH,-Math.min(livingEntity.getMaxHealth()-1,level*2), AttributeModifier.Operation.ADDITION);
        }
    };//死寂，减生命上限
    public static final Holder TROT= new Holder("trot", "2e2525d6-985c-49e8-b315-033e71fe170e") {
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            return simpleAttribute(Attributes.MOVEMENT_SPEED,level, AttributeModifier.Operation.ADDITION);
        }
    };//疾走，加移速
    public static final Holder TIRED= new Holder("tired", "5c439681-5a4f-4f09-b8e9-af1ffcc54a19") {
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            return simpleAttribute(Attributes.MOVEMENT_SPEED,-level, AttributeModifier.Operation.ADDITION);
        }
    };//疲惫，减移速
    public static final Holder FLY_UP= new Holder("fly_up", "b3937237-22a7-4a39-a093-1aba79944078") {
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            return simpleAttribute(ForgeMod.ENTITY_GRAVITY.get(),level/100f, AttributeModifier.Operation.ADDITION);
        }
    };//飞升，减少重力
    public static final Holder HEAVY= new Holder("heavy", "c4a4741f-e4c8-4c36-87c1-ceb172aeee9f") {
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            return simpleAttribute(ForgeMod.ENTITY_GRAVITY.get(),-level/100f, AttributeModifier.Operation.ADDITION);
        }
    };//厚重，增加重力
    public static final Holder FLASH= new Holder("flash", "57c42e02-89d3-4c7b-b3ae-ad2c65caa9d2") {
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            return simpleAttribute(Attributes.ATTACK_SPEED,level/4f+1, AttributeModifier.Operation.MULTIPLY_BASE);
        }
    };//快闪,加攻速
    public static final Holder INCOMPLETE= new Holder("incomplete", "ca10ab52-e048-40b2-bdd2-672caaa9fe24") {
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            return simpleAttribute(Attributes.ATTACK_SPEED,1-Math.min(1,level/4f), AttributeModifier.Operation.MULTIPLY_BASE);
        }
    };//残疾，减攻速
    public static final Holder VIRTUALIZE= new Holder("virtualize", "792619fc-6532-449b-aa2f-f67a6975df80") {
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            livingEntity.addEffect(new MobEffectInstance(MobEffectRegister.invincible.get(),400*level,level-1));
            return null;
        }
    };//虚化,间歇性无敌
    public static final Holder BLEEDING= new Holder("bleeding", "14b94de8-c865-4320-8212-237ccd409067") {
        @Nullable
        @Override
        public Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity, int level) {
            livingEntity.addEffect(new MobEffectInstance(MobEffectRegister.vulnerability.get(),20*level,0));
            return null;
        }
    };//流血，消除无敌时间
    public void applyEffect(LivingEntity trigger){
        Multimap<Attribute, AttributeModifier> attrModifMap= HashMultimap.create();
        for (Map.Entry<String, Integer> entry : attributeMap.entrySet()) {
            String attr = entry.getKey();
            Integer level = entry.getValue();
            var holder = Holder.allHolders.get(attr);
            if (holder == null) continue;
            Pair<Attribute,AttributeModifier> modifier=holder.onEffect(trigger,level);
            if(modifier!=null)attrModifMap.put(modifier.getFirst(),modifier.getSecond());
        }
        trigger.getAttributes().addTransientAttributeModifiers(attrModifMap);
    }
    protected boolean isBadAttribute(String attrA,String attrB){
        if (attrA.equals(VITALITY.name)&&attrB.equals(STILLNESS.name))return true;
        if (attrA.equals(TROT.name)&&attrB.equals(TIRED.name))return true;
        if(attrA.equals(FLASH.name)&&attrB.equals(INCOMPLETE.name)) return true;
        if(attrA.equals(VIRTUALIZE.name)&&attrB.equals(BLEEDING.name)) return true;
        return attrA.equals(FLY_UP.name) && attrB.equals(HEAVY.name);
    }
    public void putAttribute(String name,Integer level){
        boolean[] haveCrash=new boolean[]{false};
        for (Map.Entry<String, Integer> entry : attributeMap.entrySet()) {
            String attr = entry.getKey();
            Integer lv = entry.getValue();
            if (isBadAttribute(name, attr) || isBadAttribute(attr, name)) {
                haveCrash[0] = true;
                onCrashAttribute(name, level, attr, lv);
                break;
            }
        }
        if(!haveCrash[0])
            if(attributeMap.containsKey(name)){
                attributeMap.put(name,attributeMap.get(name)+level);
            }else attributeMap.put(name,level);
    }


    protected void onCrashAttribute(String attrIn,int inLevel,String attrHave,int haveLevel){
        boolean aAboveB=inLevel>haveLevel;
        if(aAboveB) {
            attributeMap.remove(attrHave);
            attributeMap.put(attrIn,inLevel-haveLevel);
        }else {
            attributeMap.computeIfPresent(attrIn, (s, integer) -> integer-inLevel);
        }
    }
    public void putMixed(Item nameSource,int count){
        var name=nameSource.getDescription().getString();
        this.putMixed(name,count);
    }
    public void putMixed(String name,int count){
        if(itemMixedNames.containsKey(name)){
            itemMixedNames.computeIfPresent(name, (s, integer) -> integer+count);
        }else {
            itemMixedNames.put(name,count);
        }
    }

    @Override
    public CompoundTag serializeNBT() {
        CompoundTag tag=new CompoundTag();
        CompoundTag attrs=new CompoundTag();
        CompoundTag mixed=new CompoundTag();
        tag.put("Attributes",attrs);
        tag.put("Mixed",mixed);
        attributeMap.forEach(attrs::putInt);
        itemMixedNames.forEach(mixed::putInt);
        return tag;
    }

    @Override
    public void deserializeNBT(CompoundTag nbt) {
        CompoundTag attrs=nbt.getCompound("Attributes");
        CompoundTag mixed=nbt.getCompound("Mixed");
        attrs.getAllKeys().forEach(key->{
            attributeMap.put(key,attrs.getInt(key));
        });
        mixed.getAllKeys().forEach(key->{
            itemMixedNames.put(key,mixed.getInt(key));
        });
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;

        if (!(o instanceof ItemMixedCapability)) return false;

        ItemMixedCapability that = (ItemMixedCapability) o;

        return new EqualsBuilder().append(attributeMap, that.attributeMap).append(itemMixedNames, that.itemMixedNames).isEquals();
    }

    @Override
    public String toString() {
        return "ItemMixedCapability{" +
                "attributeMap=" + attributeMap +
                ", itemMixedNames=" + itemMixedNames +
                '}';
    }

    @Override
    public int hashCode() {
        return new HashCodeBuilder(17, 37).append(attributeMap).append(itemMixedNames).toHashCode();
    }

    public static abstract class Holder {
        public static Map<String,Holder> allHolders=new HashMap<>();
        public final String name;
        public final UUID id;
        public final String textKey;
        public Holder(String name, String UUID) {
            this.name = name;
            textKey="yu.attribute."+name;
            this.id = TextUtils.isEmpty(UUID)?java.util.UUID.randomUUID():java.util.UUID.fromString(UUID);
            allHolders.put(name,this);
        }
        @Nullable
        public abstract Pair<Attribute,AttributeModifier> onEffect(LivingEntity livingEntity,int level);
        @Override
        public boolean equals(Object o) {
            if (this == o) return true;

            if (!(o instanceof Holder holder)) return false;

            return new EqualsBuilder().append(name, holder.name).append(id, holder.id).isEquals();
        }
        public Pair<Attribute,AttributeModifier> simpleAttribute(Attribute attr, float level, AttributeModifier.Operation operation){
            return Pair.of(attr,new AttributeModifier(id,name,level,operation));
        }

        @Override
        public String toString() {
            return "Holder{" +
                    "name='" + name + '\'' +
                    ", id=" + id +
                    ", textKey='" + textKey + '\'' +
                    '}';
        }

        @Override
        public int hashCode() {
            return new HashCodeBuilder(17, 37).append(name).append(id).toHashCode();
        }
    }
}
